//
// DotNetNuke - http://www.dotnetnuke.com
// Copyright (c) 2002-2010
// by Perpetual Motion Interactive Systems Inc. ( http://www.perpetualmotion.ca )
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and 
// to permit persons to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions 
// of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;
using DotNetNuke.Common.Utilities;
namespace DotNetNuke.UI.WebControls
{
	[ToolboxData("<{0}:PropertyEditorControl runat=server></{0}:PropertyEditorControl>"), Designer(typeof(DotNetNuke.UI.WebControls.Design.PropertyEditorControlDesigner)), PersistChildren(true)]
	public class PropertyEditorControl : WebControl, INamingContainer
	{
		public event PropertyChangedEventHandler ItemAdded;
		public event EditorCreatedEventHandler ItemCreated;
		public event PropertyChangedEventHandler ItemDeleted;
		private bool _AutoGenerate = true;
		private object _DataSource;
		private IEnumerable _UnderlyingDataSource;
		private PropertySortType _SortMode;
		private PropertyEditorMode _EditMode;
		private EditorDisplayMode _DisplayMode;
		private GroupByMode _GroupByMode;
		private HelpDisplayMode _HelpDisplayMode = HelpDisplayMode.Always;
		private LabelMode _LabelMode = LabelMode.Left;
		private Style _EditControlStyle = new Style();
		private Style _ErrorStyle = new Style();
		private Style _GroupHeaderStyle = new Style();
		private Style _HelpStyle = new Style();
		private Style _LabelStyle = new Style();
		private Style _VisibilityStyle = new Style();
		private bool _IsDirty;
		private bool _ItemChanged = false;
		private bool _GroupHeaderIncludeRule;
		private Unit _EditControlWidth;
		private Unit _LabelWidth;
		private bool _EnableClientValidation;
		private string _LocalResourceFile;
		private string _RequiredUrl;
		private bool _ShowRequired = true;
		private bool _ShowVisibility = false;
		private string _Groups = Null.NullString;
		private Hashtable _Sections;
		private ArrayList _Fields = new ArrayList();
		protected override HtmlTextWriterTag TagKey {
			get { return HtmlTextWriterTag.Div; }
		}
		protected virtual IEnumerable UnderlyingDataSource {
			get {
				if (_UnderlyingDataSource == null) {
					_UnderlyingDataSource = GetProperties();
				}
				return _UnderlyingDataSource;
			}
		}
		[Category("Behavior")]
		public bool AutoGenerate {
			get { return _AutoGenerate; }
			set { _AutoGenerate= value; }
		}
		[Browsable(false), Category("Data")]
		public object DataSource {
			get { return _DataSource; }
			set { _DataSource= value; }
		}
		[Category("Appearance")]
		public PropertyEditorMode EditMode {
			get { return _EditMode; }
			set { _EditMode= value; }
		}
		public EditorDisplayMode DisplayMode {
			get { return _DisplayMode; }
			set { _DisplayMode = value; }
		}
		[Category("Behavior")]
		public bool EnableClientValidation {
			get { return _EnableClientValidation; }
			set { _EnableClientValidation= value; }
		}
		[Category("Appearance")]
		public GroupByMode GroupByMode {
			get { return _GroupByMode; }
			set { _GroupByMode = value; }
		}
		[Category("Appearance")]
		public string Groups {
			get { return _Groups; }
			set { _Groups = value; }
		}
		public HelpDisplayMode HelpDisplayMode {
			get { return _HelpDisplayMode; }
			set { _HelpDisplayMode= value; }
		}
		[Browsable(false)]
		public bool IsDirty {
			get {
				bool _IsDirty = false;
				foreach (FieldEditorControl editor in Fields) {
					if (editor.Visible && editor.IsDirty) {
						_IsDirty = true;
						break;
					}
				}
				return _IsDirty;
			}
		}
		[Browsable(false)]
		public bool IsValid {
			get {
				bool _IsValid = true;
				foreach (FieldEditorControl editor in Fields) {
					if (editor.Visible && !editor.IsValid) {
						_IsValid = false;
						break;
					}
				}
				return _IsValid;
			}
		}
		public LabelMode LabelMode {
			get { return _LabelMode; }
			set { _LabelMode = value; }
		}
		public string LocalResourceFile {
			get { return _LocalResourceFile; }
			set { _LocalResourceFile= value; }
		}
		public string RequiredUrl {
			get { return _RequiredUrl; }
			set { _RequiredUrl= value; }
		}
		public bool ShowRequired {
			get { return _ShowRequired; }
			set { _ShowRequired= value; }
		}
		[Category("Appearance")]
		public bool ShowVisibility {
			get { return _ShowVisibility; }
			set { _ShowVisibility= value; }
		}
		[Category("Appearance")]
		public PropertySortType SortMode {
			get { return _SortMode; }
			set { _SortMode= value; }
		}
		[Category("Behavior"), PersistenceMode(PersistenceMode.InnerProperty), DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public ArrayList Fields {
			get { return _Fields; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Edit Control.")]
		public System.Web.UI.WebControls.Style EditControlStyle {
			get { return _EditControlStyle; }
		}
		[Browsable(true), Category("Appearance"), Description("Set the Width for the Edit Control.")]
		public Unit EditControlWidth {
			get { return _EditControlWidth; }
			set { _EditControlWidth= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Error Text.")]
		public System.Web.UI.WebControls.Style ErrorStyle {
			get { return _ErrorStyle; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Group Header Control.")]
		public System.Web.UI.WebControls.Style GroupHeaderStyle {
			get { return _GroupHeaderStyle; }
		}
		[Browsable(true), Category("Appearance"), Description("Set whether to include a rule <hr> in the Group Header.")]
		public bool GroupHeaderIncludeRule {
			get { return _GroupHeaderIncludeRule; }
			set { _GroupHeaderIncludeRule= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Help Text.")]
		public System.Web.UI.WebControls.Style HelpStyle {
			get { return _HelpStyle; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Label Text")]
		public System.Web.UI.WebControls.Style LabelStyle {
			get { return _LabelStyle; }
		}
		[Browsable(true), Category("Appearance"), Description("Set the Width for the Label Control.")]
		public Unit LabelWidth {
			get { return _LabelWidth; }
			set { _LabelWidth= value; }
		}
		[Browsable(true), Category("Styles"), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), TypeConverter(typeof(ExpandableObjectConverter)), PersistenceMode(PersistenceMode.InnerProperty), Description("Set the Style for the Visibility Control")]
		public System.Web.UI.WebControls.Style VisibilityStyle {
			get { return _VisibilityStyle; }
		}
		private PropertyInfo[] GetProperties()
		{
			if (DataSource != null) {
				BindingFlags Bindings = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
				PropertyInfo[] Properties = DataSource.GetType().GetProperties(Bindings);
				switch (SortMode) {
					case PropertySortType.Alphabetical:
						Array.Sort(Properties, new PropertyNameComparer());
						break;
					case PropertySortType.Category:
						Array.Sort(Properties, new PropertyCategoryComparer());
						break;
					case PropertySortType.SortOrderAttribute:
						Array.Sort(Properties, new PropertySortOrderComparer());
						break;
				}
				return Properties;
			} else {
				return null;
			}
		}
		protected void AddEditorRow(ref Table tbl, string name, IEditorInfoAdapter adapter)
		{
			TableRow row = new TableRow();
			tbl.Rows.Add(row);
			TableCell cell = new TableCell();
			row.Cells.Add(cell);
			FieldEditorControl editor = new FieldEditorControl();
			editor.DataSource = DataSource;
			editor.EditorInfoAdapter = adapter;
			editor.DataField = name;
			editor.EditorDisplayMode = DisplayMode;
			editor.EnableClientValidation = EnableClientValidation;
			editor.EditMode = EditMode;
			editor.HelpDisplayMode = HelpDisplayMode;
			editor.LabelMode = LabelMode;
			editor.LabelWidth = LabelWidth;
			editor.LabelStyle.CopyFrom(LabelStyle);
			editor.HelpStyle.CopyFrom(HelpStyle);
			editor.ErrorStyle.CopyFrom(ErrorStyle);
			editor.VisibilityStyle.CopyFrom(VisibilityStyle);
			editor.EditControlStyle.CopyFrom(EditControlStyle);
			editor.EditControlWidth = EditControlWidth;
			editor.LocalResourceFile = LocalResourceFile;
			editor.RequiredUrl = RequiredUrl;
			editor.ShowRequired = ShowRequired;
			editor.ShowVisibility = ShowVisibility;
			editor.Width = Width;
			editor.ItemAdded += this.CollectionItemAdded;
			editor.ItemChanged += this.ListItemChanged;
			editor.ItemCreated += this.EditorItemCreated;
			editor.ItemDeleted += this.CollectionItemDeleted;
			editor.DataBind();
			Fields.Add(editor);
			cell.Controls.Add(editor);
		}
		protected virtual void AddEditorRow(ref Table tbl, object obj)
		{
			PropertyInfo objProperty = (PropertyInfo)obj;
			AddEditorRow(ref tbl, objProperty.Name, new StandardEditorInfoAdapter(DataSource, objProperty.Name));
		}
		protected virtual void AddFields(Table tbl)
		{
			foreach (FieldEditorControl editor in Fields) {
				TableRow row = new TableRow();
				tbl.Rows.Add(row);
				TableCell cell = new TableCell();
				row.Cells.Add(cell);
				editor.EditorDisplayMode = DisplayMode;
				editor.EditorInfoAdapter = new StandardEditorInfoAdapter(DataSource, editor.DataField);
				editor.EnableClientValidation = EnableClientValidation;
				if (editor.EditMode != PropertyEditorMode.View) {
					editor.EditMode = EditMode;
				}
				editor.HelpDisplayMode = HelpDisplayMode;
				if (editor.LabelMode == LabelMode.None) {
					editor.LabelMode = LabelMode;
				}
				if (editor.LabelWidth == Unit.Empty) {
					editor.LabelWidth = LabelWidth;
				}
				editor.LabelStyle.CopyFrom(LabelStyle);
				editor.HelpStyle.CopyFrom(HelpStyle);
				editor.ErrorStyle.CopyFrom(ErrorStyle);
				editor.VisibilityStyle.CopyFrom(VisibilityStyle);
				editor.EditControlStyle.CopyFrom(EditControlStyle);
				if (editor.EditControlWidth == Unit.Empty) {
					editor.EditControlWidth = EditControlWidth;
				}
				editor.LocalResourceFile = LocalResourceFile;
				editor.RequiredUrl = RequiredUrl;
				editor.ShowRequired = ShowRequired;
				editor.ShowVisibility = ShowVisibility;
				editor.Width = Width;
				editor.ItemAdded += this.CollectionItemAdded;
				editor.ItemChanged += this.ListItemChanged;
				editor.ItemCreated += this.EditorItemCreated;
				editor.ItemDeleted += this.CollectionItemDeleted;
				editor.DataSource = DataSource;
				editor.DataBind();
				cell.Controls.Add(editor);
			}
		}
		protected virtual void AddHeader(ref Table tbl, string header)
		{
			Panel panel = new Panel();
			System.Web.UI.WebControls.Image icon = new System.Web.UI.WebControls.Image();
			icon.ID = "ico" + header;
			icon.EnableViewState = false;
			System.Web.UI.WebControls.Literal spacer = new System.Web.UI.WebControls.Literal();
			spacer.Text = " ";
			spacer.EnableViewState = false;
			Label label = new Label();
			label.ID = "lbl" + header;
			label.Attributes["resourcekey"] = this.ID + "_" + header + ".Header";
			label.Text = header;
			label.EnableViewState = false;
			label.ControlStyle.CopyFrom(GroupHeaderStyle);
			panel.Controls.Add(icon);
			panel.Controls.Add(spacer);
			panel.Controls.Add(label);
			if (GroupHeaderIncludeRule) {
				panel.Controls.Add(new LiteralControl("<hr noshade=\"noshade\" size=\"1\"/>"));
			}
			Controls.Add(panel);
			if (_Sections == null) {
				_Sections = new Hashtable();
			}
			_Sections[icon] = tbl;
		}
		protected virtual void CreateEditor()
		{
			Table tbl;
			string[] arrGroups = null;
			Controls.Clear();
			if (!String.IsNullOrEmpty(Groups)) {
				arrGroups = Groups.Split(',');
			} else if (GroupByMode != GroupByMode.None) {
				arrGroups = GetGroups(UnderlyingDataSource);
			}
			if (!AutoGenerate) {
				tbl = new Table();
				tbl.ID = "tbl";
				AddFields(tbl);
				Controls.Add(tbl);
			} else {
				Fields.Clear();
				if (arrGroups != null && arrGroups.Length > 0)
				{
					foreach (string strGroup in arrGroups) {
						if (GroupByMode == UI.WebControls.GroupByMode.Section) {
							tbl = new Table();
							tbl.ID = "tbl" + strGroup;
							foreach (object obj in UnderlyingDataSource) {
								if (GetCategory(obj) == strGroup.Trim()) {
									if (GetRowVisibility(obj)) {
										if (tbl.Rows.Count == 0) {
											AddHeader(ref tbl, strGroup);
										}
										AddEditorRow(ref tbl, obj);
									}
								}
							}
							if (tbl.Rows.Count > 0) {
								Controls.Add(tbl);
							}
						}
					}
				} else {
					tbl = new Table();
					tbl.ID = "tbl";
					foreach (object obj in UnderlyingDataSource) {
						if (GetRowVisibility(obj)) {
							AddEditorRow(ref tbl, obj);
						}
					}
					Controls.Add(tbl);
				}
			}
		}
		protected virtual string GetCategory(object obj)
		{
			PropertyInfo objProperty = (PropertyInfo)obj;
			string _Category = Null.NullString;
			object[] categoryAttributes = objProperty.GetCustomAttributes(typeof(CategoryAttribute), true);
			if (categoryAttributes.Length > 0)
			{
				CategoryAttribute category = (CategoryAttribute)categoryAttributes[0];
				_Category = category.Category;
			}
			return _Category;
		}
		protected virtual string[] GetGroups(IEnumerable arrObjects)
		{
			ArrayList arrGroups = new ArrayList();
			
			foreach (PropertyInfo objProperty in arrObjects) {
				object[] categoryAttributes = objProperty.GetCustomAttributes(typeof(CategoryAttribute), true);
				if (categoryAttributes.Length > 0)
				{
					CategoryAttribute category = (CategoryAttribute)categoryAttributes[0];
					if (!arrGroups.Contains(category.Category)) {
						arrGroups.Add(category.Category);
					}
				}
			}
			string[] strGroups = new string[arrGroups.Count];
			for (int i = 0; i <= arrGroups.Count - 1; i++) {
				strGroups[i] = Convert.ToString(arrGroups[i]);
			}
			return strGroups;
		}
		protected virtual bool GetRowVisibility(object obj)
		{
			PropertyInfo objProperty = (PropertyInfo)obj;
			bool isVisible = true;
			object[] browsableAttributes = objProperty.GetCustomAttributes(typeof(BrowsableAttribute), true);
			if (browsableAttributes.Length > 0)
			{
				BrowsableAttribute browsable = (BrowsableAttribute)browsableAttributes[0];
				if (!browsable.Browsable) {
					isVisible = false;
				}
			}
			if (!isVisible && EditMode == PropertyEditorMode.Edit) {
				object[] requiredAttributes = objProperty.GetCustomAttributes(typeof(RequiredAttribute), true);
				if (requiredAttributes.Length > 0)
				{
					RequiredAttribute required = (RequiredAttribute)requiredAttributes[0];
					if (required.Required) {
						isVisible = true;
					}
				}
			}
			return isVisible;
		}
		protected virtual void OnItemAdded(PropertyEditorEventArgs e)
		{
			if (ItemAdded != null) {
				ItemAdded(this, e);
			}
		}
		protected virtual void OnItemCreated(PropertyEditorItemEventArgs e)
		{
			if (ItemCreated != null) {
				ItemCreated(this, e);
			}
		}
		protected virtual void OnItemDeleted(PropertyEditorEventArgs e)
		{
			if (ItemDeleted != null) {
				ItemDeleted(this, e);
			}
		}
		protected override void OnPreRender(System.EventArgs e)
		{
			if (_ItemChanged) {
				DataBind();
			}
			if (GroupByMode == UI.WebControls.GroupByMode.Section && (_Sections != null)) {
				foreach (DictionaryEntry key in _Sections) {
					Table tbl = (Table)key.Value;
					System.Web.UI.WebControls.Image icon = (System.Web.UI.WebControls.Image)key.Key;
					DotNetNuke.UI.Utilities.DNNClientAPI.EnableMinMax(icon, tbl, false, Page.ResolveUrl("~/images/minus.gif"), Page.ResolveUrl("~/images/plus.gif"), Utilities.DNNClientAPI.MinMaxPersistanceType.Page);
				}
			}
			base.OnPreRender(e);
		}
		public override void DataBind()
		{
			base.OnDataBinding(EventArgs.Empty);
			Controls.Clear();
			ClearChildViewState();
			TrackViewState();
			CreateEditor();
			ChildControlsCreated = true;
		}
		protected virtual void CollectionItemAdded(object sender, PropertyEditorEventArgs e)
		{
			OnItemAdded(e);
		}
		protected virtual void CollectionItemDeleted(object sender, PropertyEditorEventArgs e)
		{
			OnItemDeleted(e);
		}
		protected virtual void EditorItemCreated(object sender, PropertyEditorItemEventArgs e)
		{
			OnItemCreated(e);
		}
		protected virtual void ListItemChanged(object sender, PropertyEditorEventArgs e)
		{
			_ItemChanged = true;
		}
	}
}
